﻿/* Skype AutoConference by Ing. Lele
 * Copyright (C) 2008 Ing. Lele
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *     
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *  
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using SKYPE4COMLib;

namespace Skype_AutoConference
{
    public partial class frmMain : Form
    {
        // Some flags to check our status information
        public bool Attached = false; // If we are attached to skype
        public bool IsOnline = false; // If user is online
        public bool WasAttached = false; // If we lost attach to skype
        public bool PendingSilentModeStartup = false; // Silent Mode is active

        // Uses the normal Skype4COMLib using statement
        public Skype skype = new Skype();

        // Use this for ambiguous reference situations like
        // Attachment Status, Connection Status and others
        public SKYPE4COMLib.SkypeClass cSkype;

        public frmMain()
        {
            // Create objects on frmMain
            InitializeComponent();

            // Note: These use the Skype using reference
            skype.UserStatus += new _ISkypeEvents_UserStatusEventHandler(Skype_UserStatus);
            skype.CallStatus += new _ISkypeEvents_CallStatusEventHandler(Skype_CallStatus);
            skype.MessageStatus += new _ISkypeEvents_MessageStatusEventHandler(Skype_MessageStatus);

            // Note: These use the Skype class reference due to ambiguous reference issues
            cSkype = new SkypeClass();
            cSkype._ISkypeEvents_Event_AttachmentStatus += new _ISkypeEvents_AttachmentStatusEventHandler(Skype_AttachmentStatus);
            cSkype._ISkypeEvents_Event_ConnectionStatus += new _ISkypeEvents_ConnectionStatusEventHandler(Skype_ConnectionStatus);

            // Used to catch our form closing so we can save checkbox information
            FormClosing += SaveSettingsOnClose;
        }
        private void btnClearMsgLog_Click(object sender, EventArgs e)
        {
            // Clear the Message Log textbox
            txtMsgLog.Text = "- Clear -";
        }
        private void btnSilentMode_Click(object sender, EventArgs e)
        {
            // Enable the Silent Mode for Skype
            Skype_SilentMode(skype, Attached);
        }
        private void chkExitWithSkype_CheckedChanged(object sender, EventArgs e)
        {
            // Change the "Exit with Skype" status option
            if (this.chkExitWithSkype.Checked)
            {
                Properties.Settings.Default.StopAtLogoffOrExit = true;
            }
            else
            {
                Properties.Settings.Default.StopAtLogoffOrExit = false;
            }
        }
        private void chkStartSkype_CheckedChanged(object sender, EventArgs e)
        {
            // Change the "Start Skype" status option
            if (this.chkStartSkype.Checked)
            {
                Properties.Settings.Default.StartSkype = true;
            }
            else
            {
                Properties.Settings.Default.StartSkype = false;
            }
        }
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Exit the program
            System.Windows.Forms.Application.Exit();
        }
        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Show frmAbout in Modal mode
            using (frmAbout xForm = new frmAbout())
            {
                xForm.ShowDialog(this);
            }
        }
        private void keywordToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Set the keywork to start the conference
            System.Windows.Forms.MessageBox.Show("CONF is the keyword!");
        }
        private void pinToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Set the PIN to confirm the authentication
            System.Windows.Forms.MessageBox.Show("1234 is the PIN!");
        }
        private void guideToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Open Guide webpage
            try
            {
                System.Diagnostics.Process.Start("http://inglele.blogspot.com");
            }
            catch (Exception)
            {
                // Just In case User is offline with no internet connection
            }
        }
        public void SaveSettingsOnClose(object sender, FormClosingEventArgs e)
        {
            // User Settings are saved here (Check Boxes)
            Properties.Settings.Default.Save();
        }
        public void Example_Initialize(object sender, System.EventArgs e)
        {
            // Initialize options from Saved User Settings
            this.chkStartSkype.Checked = Properties.Settings.Default.StartSkype;
            this.chkExitWithSkype.Checked = Properties.Settings.Default.StopAtLogoffOrExit;
            this.textBoxUserPIN.Text = Properties.Settings.Default.UserPIN.ToString();
            this.textboxBotChar.Text = Properties.Settings.Default.BotChar.ToString();
            this.textBoxBotCMDconference.Text = Properties.Settings.Default.BotCMD_Conference;

            try
            {
                // This attaches to the Skype4COM class statement
                cSkype.Attach(7, false);
            }
            catch (Exception)
            {
                // All Skype Logic uses TRY for safety
            }

            // Check to See if we are going to start Skype based on CheckBox setting

            if (Properties.Settings.Default.StartSkype)
            {
                try
                {
                    if (!skype.Client.IsRunning)
                    {
                        skype.Client.Start(false, true);
                    }
                }
                catch (Exception)
                {
                    // All Skype Logic uses TRY for safety
                }
            }
        }
        public void Skype_AttachmentStatus(TAttachmentStatus status)
        {
            Attached = false;

            // Write Attachment Status to Window
            this.txtMsgLog.AppendText("Attachment Status: " + cSkype.Convert.AttachmentStatusToText(status));
            this.txtMsgLog.AppendText(" - " + status.ToString() + Environment.NewLine);
            this.txtMsgLog.ScrollToCaret();
            
            if (status == TAttachmentStatus.apiAttachAvailable)
            {
                try
                {
                    // This attaches to the Skype4COM class statement
                    cSkype.Attach(7, true);
                }
                catch (Exception)
                {
                    // All Skype Logic uses TRY for safety
                }
            }
            else
                if (status == TAttachmentStatus.apiAttachSuccess)
                {
                    try
                    {
                        // This attaches to the Skype using reference. We already sent an attach request
                        // for the Skype class in the initialization procedure, which is how this
                        // Attachment success was created. So now that we know we are connected
                        // to the Skype Client, we need to Attach to the Skype Class as well.

                        // If we don't attach to both the Skype using reference and the Skype class
                        // reference we will never get User, Call, or Message events. This can best
                        // be seen by starting Skype and then this Example application. You can see
                        // that when you change your User status, Make a Call, Send or receive chat
                        // Messages, that none of those events will be processed if this was commented
                        // out.

                        // It should be noted that this does cause a slight delay with the GUI when
                        // you start this example program without the DoEvents below.

                        System.Windows.Forms.Application.DoEvents();
                        skype.Attach(7, false);
                    }
                    catch (Exception)
                    {
                        // All Skype Logic uses TRY for safety
                    }

                    Attached = true;
                    WasAttached = true;

                    // If we have a queued Silent Mode request, We are attached, process it now
                    if (PendingSilentModeStartup)
                    {
                        PendingSilentModeStartup = false;
                        try
                        {
                            if (!skype.SilentMode) skype.SilentMode = true;
                        }
                        catch (Exception)
                        {
                            // All Skype Logic uses TRY for safety
                        }
                    }
                }
                else
                    if ((status == TAttachmentStatus.apiAttachNotAvailable) && (Properties.Settings.Default.StopAtLogoffOrExit) && (WasAttached))
                    {
                        this.Close();
                    }
        }
        public void Skype_ConnectionStatus(TConnectionStatus status)
        {
            IsOnline = false;

            // Write Connection Status to Window
            this.txtMsgLog.AppendText("Connection Status: " + cSkype.Convert.ConnectionStatusToText(status));
            this.txtMsgLog.AppendText(" - " + status.ToString() + Environment.NewLine);
            this.txtMsgLog.ScrollToCaret();
            
            if (status == TConnectionStatus.conOnline)
            {
                IsOnline = true;
            }
        }
        public void Skype_UserStatus(TUserStatus status)
        {
            // Write User Status to Window
            this.txtMsgLog.AppendText("User Status: " + skype.Convert.UserStatusToText(status));
            this.txtMsgLog.AppendText(" - " + status.ToString() + Environment.NewLine);
            this.txtMsgLog.ScrollToCaret();
            
            if ((status == TUserStatus.cusLoggedOut) && (Properties.Settings.Default.StopAtLogoffOrExit))
            {
                this.Close();
            }
        }
        public void Skype_CallStatus(Call call, TCallStatus status)
        {
            // Write Call Status to Window
            this.txtMsgLog.AppendText("Call Status: " + skype.Convert.CallStatusToText(status));
            this.txtMsgLog.AppendText(" - " + status.ToString() + Environment.NewLine);
            this.txtMsgLog.ScrollToCaret();
            
            if ((status > TCallStatus.clsUnplaced) && (status < TCallStatus.clsOnHold) && (status != TCallStatus.clsFailed))
            {
                // Could Start or Stop something when a call starts here
            }
            else
            {
                // Could Stop or Resume something when a call stops here
                call.Finish();
            }
        }
        public void Skype_SilentMode(Skype skype, bool Attached)
        {
            // Enable the Silent Mode for Skype
            try
            {
                if ((skype.Client.IsRunning) && (Attached))
                {
                    if (!skype.SilentMode)
                    {
                        skype.SilentMode = true;
                    }
                }
                else
                    if (!Attached)
                    {
                        PendingSilentModeStartup = true;
                        if ((!skype.Client.IsRunning) && (Properties.Settings.Default.StartSkype)) skype.Client.Start(false, true);
                    }
            }
            catch (Exception)
            {
                // All Skype Logic uses TRY for safety
            }
        }
        public void Skype_MessageStatus(IChatMessage ichatmessage, TChatMessageStatus Status)
        {
            // Check Message & Execute command
            try
            {
                // Check if status is cmsReceived
                if (Status == SKYPE4COMLib.TChatMessageStatus.cmsReceived)
                {
                    // Create result string
                    string[] skypeMsgSplit = {"","",""};

                    // Check valid message ---> Look for valid data {Command, PIN, parameters}
                    if (ichatmessage.Body.ToString() != "") skypeMsgSplit = Skype_ValidCommand(ichatmessage.Body.ToString());

                    /********************************\
                    |*      CONFERENCE Command      *|
                    \********************************/
                    if (skypeMsgSplit[1].ToUpper() == Properties.Settings.Default.BotCMD_Conference) 
                    {
                        // Get Skype User structure from username
                        SKYPE4COMLib.IUser iskypeuser = Skype_GetUser(skypeMsgSplit[2]);

                        // Check if user is null
                        if (iskypeuser == null || iskypeuser.Handle == "") 
                        {
                            // No user found!

                            // Reply in chat
                            AddMsgChat("Username is invalid!", ichatmessage.Chat);
                            AddMsgChat("Remark: only known friends are allowed for the conference", ichatmessage.Chat);
                            
                            // Updated Text Log
                            AddMsgLog("Time", ichatmessage.Timestamp.ToString());  // Display Time
                            AddMsgLog("COMMAND", "---> Invalid username!"); // Report invalid username
                        }
                        else
                        {
                            // Reply in chat
                            AddMsgChat("Command OK", ichatmessage.Chat);
                            AddMsgChat("Executing conference call", ichatmessage.Chat);
                            
                            // Updated Text Log
                            AddMsgLog("Time", ichatmessage.Timestamp.ToString());  // Display Time
                            AddMsgLog("COMMAND", "---> Conference executing"); // Report command execution
                            AddMsgLog("Participant", iskypeuser.Handle.ToString());  // Display Name of participant
                            AddMsgLog("Sender", ichatmessage.FromHandle.ToString());    // Show Skypename 

                            // Execute Conference command
                            SKYPE4COMLib.ICall skypeConferenceCall = Skype_MakeConference(ichatmessage.Sender, iskypeuser, ichatmessage);

                            // Reply in chat
                            AddMsgChat("Conference OK", ichatmessage.Chat);
                            
                            // Updated Text Log
                            AddMsgLog("COMMAND", "---> Conference done"); // Report command execution
                            AddMsgLog("ID", skypeConferenceCall.ConferenceId.ToString());  // Display Conference ID
                            AddMsgLog("Status", skypeConferenceCall.Status.ToString());    // Display Conference Status
                        }
                    }
                    
                        // Wrong PIN
                    else if (skypeMsgSplit[1].ToUpper() == "PIN")
                    {
                        // Reply in chat
                        AddMsgChat("Invalid PIN!", ichatmessage.Chat); // Wrong PIN

                        // Updated Text Log
                        AddMsgLog("Time", ichatmessage.Timestamp.ToString());  // Display Time
                        AddMsgLog("COMMAND", "---> Invalid PIN!"); // Report invalid PIN
                    }

                        // Invalid Command
                    else if (skypeMsgSplit[1].ToUpper() == "INVALID")
                    {
                        // Reply in chat
                        AddMsgChat("Wrong Command!", ichatmessage.Chat); // Wrong Command

                        // Updated Text Log
                        AddMsgLog("Time", ichatmessage.Timestamp.ToString());  // Display Time
                        AddMsgLog("COMMAND", "---> Invalid Command!"); // Report invalid command
                    }

                    // Default & "BOT"
                    else                         
                    {
                        // Updated Text Log
                        // AddMsgLog("COMMAND", "Skipped message"); // Report ignored message
                    }
                }
                else
                {
                    // Status not cmsReceived --> Ignore message
                }
            }
            catch (Exception)
            {
                // All Skype Logic uses TRY for safety
            }

            // Write list of chat users
            
            foreach (SKYPE4COMLib.IUser SkypeUser in ichatmessage.Users)
            {
                 AddMsgLog("User          ", SkypeUser.Handle);
            }
        }
        public SKYPE4COMLib.ICall Skype_MakeConference(SKYPE4COMLib.IUser skypeSender, SKYPE4COMLib.IUser skypeFriend, SKYPE4COMLib.IChatMessage skypeCommandMsg)
        {
            // Create the conference call
        
            // Check if skypeSender is offline or unknown
            if ( (skypeSender.OnlineStatus == SKYPE4COMLib.TOnlineStatus.olsOffline) || (skypeSender.OnlineStatus == SKYPE4COMLib.TOnlineStatus.olsUnknown) )
            {
                // Cancel the conference
                // Reply in chat
                AddMsgChat(skypeSender.Handle.ToString() + " is offline", skypeCommandMsg.Chat); // Report user offline
                AddMsgChat("Conference cancelled!", skypeCommandMsg.Chat); // Report conference cancelled

                // Updated Text Log
                AddMsgLog("COMMAND", "---> " + skypeSender.Handle.ToString() + "offline, conference cancelled!"); // Report conference cancelled
            }

            // Check if skypeFriend is offline or unknown
            if ( (skypeFriend.OnlineStatus == SKYPE4COMLib.TOnlineStatus.olsOffline) || (skypeFriend.OnlineStatus == SKYPE4COMLib.TOnlineStatus.olsUnknown) )
            {
                // Cancel the conference
                // Reply in chat
                AddMsgChat(skypeFriend.Handle.ToString() + " is offline", skypeCommandMsg.Chat); // Report user offline
                AddMsgChat("Conference cancelled!", skypeCommandMsg.Chat); // Report conference cancelled

                // Updated Text Log
                AddMsgLog("COMMAND", "---> " + skypeFriend.Handle.ToString() + " offline, conference cancelled!"); // Report conference cancelled
            }

            // Set my state to online
            this.skype.ChangeUserStatus(SKYPE4COMLib.TUserStatus.cusOnline);

            // Create Skype call: skypeSender & BOT
            SKYPE4COMLib.ICall skypeConferenceCall = this.skype.PlaceCall(skypeSender.Handle, skypeFriend.Handle,"", "");
            // SKYPE4COMLib.ICall skypeConferenceCall = this.skype.PlaceCall ( this.skype.CurrentUser.Handle, skypeSender.Handle, skypeFriend.Handle, "");

            // Return Conference object
            return skypeConferenceCall;
        }
        public SKYPE4COMLib.IUser Skype_GetUser(string skypeUsername)
        {
            // Get iUser structure from skypeUsername

            // Get a list of Skype users for skypeUsername
            SKYPE4COMLib.IUserCollection skypeUsersResult = this.skype.SearchForUsers(skypeUsername);

            // Check number of skypeUsersResult
            if (skypeUsersResult.Count == 1)
            {
                // Only 1 user --> Return first one
                return skypeUsersResult[1];
            }
            if (skypeUsersResult.Count < 1)
            {
                // No Skype user found, report null
                return null;
            }
            if (skypeUsersResult.Count > 1)
            {
                // Look for first friend to match skypeUsername

                // Repeat for skypeUsersResult --> Return first friend
                foreach (SKYPE4COMLib.IUser skypeUser in skypeUsersResult)
                {
                    // Check BuddyStatus == budFriend (indicates the contact is in a contact list)
                    if (skypeUser.BuddyStatus == SKYPE4COMLib.TBuddyStatus.budFriend) return skypeUser;
                }
                // No friends found in skypeUsersResult --> Return null
                return null;
            }
            else
            {
                // Username is not a known friends --> Report null!
                return null;
            }
        }
        public string[] Skype_ValidCommand(string RawChatMessage)
        {
            // Check if RawChatMessage contains valid Command, PIN, parameters
            try
            {
                // Check if start with BOT Char
                if (Properties.Settings.Default.BotChar!= RawChatMessage[0]) return new String[] {"0,BOT,0"};

                // Remove Properties.Settings.Default.BotChar
                RawChatMessage = RawChatMessage.Substring(1, RawChatMessage.Length-1);

                // Split the message into substring
                string[] skypeMsgSplit = RawChatMessage.Split(new Char[] { ' ', ',', '.', ':' });

                // Look for valid PIN
                if (skypeMsgSplit[0] != Properties.Settings.Default.UserPIN)
                {
                    // Update return value
                    skypeMsgSplit[1] = "PIN";

                    // Return error result
                    return skypeMsgSplit;
                }

                // Look for valid command

                // Look for valid skypename

                return skypeMsgSplit;
            }
            catch (Exception)
            {
                // All Skype Logic uses TRY for safety
                return new String[] { "0,BOT,0" };
            }
        }
        private void AddMsgLog(string LogDescription, string LogMessage)
        {
            // Add a log entry to the frmMain.txtMsgLog
            this.txtMsgLog.AppendText(LogDescription + ": " + LogMessage + Environment.NewLine);
            // this.txtMsgLog.AppendText("=================================" + Environment.NewLine);
            this.txtMsgLog.ScrollToCaret();
        }
        private void AddMsgChat(string ChatMessage, SKYPE4COMLib.Chat SkypeChatID)
        {
            // Send a reply message to SkypeChatID
            SkypeChatID.SendMessage(ChatMessage);
        }
        private void textboxBotChar_TextChanged(object sender, EventArgs e)
        {
            // Check maximum size of 1 char
            if (this.textboxBotChar.Text.Length != 1)
            {
                // Show MsgBox to reduce 
                System.Windows.Forms.MessageBox.Show("The value is not valid!" + Environment.NewLine + "You can set only 1 character.", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                // Clear the value
                this.textboxBotChar.Text = Properties.Settings.Default.BotChar.ToString();

                // Set focus on textbox
                this.textboxBotChar.Focus();
            }
            else
            {
                // Save new settings
                Properties.Settings.Default.BotChar= this.textboxBotChar.Text.ToCharArray()[0];

                // Save log
                this.AddMsgLog("Settings", "Updated BOT char to: " + Properties.Settings.Default.BotChar.ToString());
            }
        }
        private void textBoxUserPIN_TextChanged(object sender, EventArgs e)
        {
            // Check if PIN is OK
            if (this.textBoxUserPIN.Text.Length < 1 || this.textBoxUserPIN.Text.Length > 8)
            {
                // Show MsgBox to reduce string size
                System.Windows.Forms.MessageBox.Show("The value is not valid!" + Environment.NewLine + "Set a length between 1 and 8 characters.", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                // Clear the value
                this.textBoxUserPIN.Text = Properties.Settings.Default.UserPIN;

                // Set focus on textbox
                this.textBoxUserPIN.Focus();
            }
            else
            {
                // Save new settings
                Properties.Settings.Default.UserPIN = this.textBoxUserPIN.Text;

                // Save log
                this.AddMsgLog("Settings", "Updated user PIN to: " + Properties.Settings.Default.UserPIN.ToString());
            }
        }
        private void textBoxBotCMDconference_TextChanged(object sender, EventArgs e)
        {
            // Check if PIN is OK
            if (this.textBoxBotCMDconference.Text.Length < 1 || this.textBoxBotCMDconference.Text.Length > 8)
            {
                // Show MsgBox to reduce string size
                System.Windows.Forms.MessageBox.Show("The value is not valid!" + Environment.NewLine + "Set a length between 1 and 8 characters.", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                // Clear the value
                this.textBoxBotCMDconference.Text = Properties.Settings.Default.BotCMD_Conference;

                // Set focus on textbox
                this.textBoxBotCMDconference.Focus();
            }
            else
            {
                // Save new settings
                Properties.Settings.Default.BotCMD_Conference = this.textBoxBotCMDconference.Text;

                // Save log
                this.AddMsgLog("Settings", "Updated BOT conference command to: " + Properties.Settings.Default.BotCMD_Conference.ToString());
            }
        }
    }
}